#include "rpc_stub.h"
#include "object_pool.h"
#include "rpc_logger.h"
#include "rpc_root.h"
#include "rpc_statistics_impl.hh"
#include <chrono>

namespace rpc {

static std::string NullString;

std::time_t getMicroseconds() {
#if (defined(WIN32) || defined(_WIN64))
  LARGE_INTEGER freq;
  LARGE_INTEGER fc;
  if (!QueryPerformanceFrequency(&freq)) {
    return 0;
  }
  if (!QueryPerformanceCounter(&fc)) {
    return 0;
  }
  return fc.QuadPart / (freq.QuadPart / 1000 / 1000);
#else
  struct timeval tv;
  std::time_t ms;
  gettimeofday(&tv, 0);
  ms = tv.tv_sec * 1000 * 1000;
  ms += tv.tv_usec;
  return ms;
#endif /* defined(WIN32) || defined(_WIN64) */
}

StubImpl::~StubImpl() { factory_->onStubDestroy(); }

void StubCallImpl::setData(TransportPtr &transport, int size) {
  if (!transport || transport->isClose()) {
    return;
  }
  if (data_) {
    rpc_->free(data_);
  }
  data_ = (char *)rpc_->malloc(size);
  dataSize_ = size;
  transport->recv(data_, size);
}

const char *StubCallImpl::getData(int &size) {
  size = dataSize_;
  return data_;
}

MethodID StubCallImpl::getMethodID() { return methodID_; }

CallID StubCallImpl::getCallID() { return callID_; }

TransportPtr StubCallImpl::getTransport() { return transport_; }

ServiceID StubCallImpl::getServiceID() { return serviceID_; }

ServiceUUID StubCallImpl::getServiceUUID() { return serviceUUID_; }

bool StubCallImpl::isFinished() { return finished_; }

void StubCallImpl::setTransport(TransportPtr transport) {
  transport_ = transport;
}

void StubCallImpl::setTimerHandle(TimerHandle timerHandle) {
  timerHandle_ = timerHandle;
}

TimerHandle StubCallImpl::getTimerHandle() { return timerHandle_; }

void StubCallImpl::setCoroID(coroutine::CoroID coroID) { coroID_ = coroID; }

coroutine::CoroID StubCallImpl::getCoroID() { return coroID_; }

void StubCallImpl::setCallID(CallID callID) { callID_ = callID; }

void StubCallImpl::setMethodID(MethodID methodID) { methodID_ = methodID; }

void StubCallImpl::setServiceID(ServiceID serviceID) { serviceID_ = serviceID; }

void StubCallImpl::setServiceUUID(ServiceUUID serviceUUID) {
  serviceUUID_ = serviceUUID;
}

void StubCallImpl::setStubCallID(StubCallID stubCallID) {
  stubCallID_ = stubCallID;
}

StubCallID StubCallImpl::getStubCallID() { return stubCallID_; }

bool StubCallImpl::isCoroMode() {
  return coroID_ != coroutine::INVALID_CORO_ID;
}

void StubImpl::callCoro(StubCallPtr &stubCall) {
  rpc_->getStubCallManager()->add(stubCall);
  auto stubCallID = stubCall->getStubCallID();
  auto coroID = coro_spawn(
      [&](void *userData) {
        auto stubCallID =
            static_cast<StubCallID>(reinterpret_cast<std::uint64_t>(userData));
        auto stubCall = rpc_->getStubCallManager()->get(stubCallID);
        if (!stubCall) {
          return;
        }
        auto stub =
            rpc_->getStubManager()->getServiceByID(stubCall->getServiceID());
        if (!stub) {
          rpc_->getStubCallManager()->destroy(stubCallID);
          return;
        }
        auto stubImpl = std::dynamic_pointer_cast<StubImpl>(stub);
        if (stubCall->getTransport()->isClose()) {
          stubImpl->decref();
          rpc_->getStubCallManager()->destroy(stubCallID);
          return;
        }
        auto isOpenStat = rpc_->isOpenStat();
        std::time_t start = 0;
        if (isOpenStat) {
          start = getMicroseconds();
        }
        try {
          stub->call(stubCall);
          if (isOpenStat) {
            auto end = getMicroseconds();
            getStatisticImpl()->addStatistics(
                stub->getUUID(), stubCall->getMethodID(), start, end,
                stub->getMethodName(stubCall->getMethodID()), true);
          }
        } catch (std::exception &e) {
          Logbegin << "Call "
                   << "[" << stub->getServiceName() << ":"
                   << stub->getMethodName(stubCall->getMethodID())
                   << "] exception thrown:" << e.what() << Logend;
          if (isOpenStat) {
            auto end = getMicroseconds();
            getStatisticImpl()->addStatistics(
                stub->getUUID(), stubCall->getMethodID(), start, end,
                stub->getMethodName(stubCall->getMethodID()), true);
          }
        }
        rpc_->getStubCallManager()->destroy(stubCallID);
        stubImpl->decref();
      },
      reinterpret_cast<void *>(stubCallID));
  if (coroID == coroutine::INVALID_CORO_ID) {
    Logbegin << "Call "
             << "[" << getServiceName() << ":"
             << getMethodName(stubCall->getMethodID())
             << "] failed, start coroutine failed" << Logend;
    rpc_->getStubCallManager()->destroy(stubCallID);
    decref();
    return;
  }
  auto stubCallImpl = std::dynamic_pointer_cast<StubCallImpl>(stubCall);
  stubCallImpl->setCoroID(coroID);
  stubCallImpl->setStartTime(getMicroseconds());
  stubCallImpl->startExecTimer();
}

void StubImpl::callNormal(StubCallPtr &stubCall) {
  rpc_->getStubCallManager()->add(stubCall);
  auto stubCallID = stubCall->getStubCallID();
  auto isOpenStat = rpc_->isOpenStat();
  std::time_t start = 0;
  if (isOpenStat) {
    start = getMicroseconds();
  }
  auto stubCallImpl = std::dynamic_pointer_cast<StubCallImpl>(stubCall);
  try {
    call(stubCall);
    if (stubCallImpl->isFinished() && isOpenStat) {
      auto end = getMicroseconds();
      getStatisticImpl()->addStatistics(
          getUUID(), stubCall->getMethodID(), start, end,
          getMethodName(stubCall->getMethodID()), true);
    }
  } catch (std::exception &e) {
    Logbegin << "Call "
             << "[" << getServiceName() << ":"
             << getMethodName(stubCall->getMethodID())
             << "] exception thrown:" << e.what() << Logend;
    rpc_->getStubCallManager()->destroy(stubCallID);
    decref();
    if (isOpenStat) {
      auto end = getMicroseconds();
      getStatisticImpl()->addStatistics(
          getUUID(), stubCall->getMethodID(), start, end,
          getMethodName(stubCall->getMethodID()), true);
    }
    return;
  }
  if (stubCallImpl->isFinished()) {
    rpc_->getStubCallManager()->destroy(stubCallID);
    decref();
    return;
  } else {
    stubCallImpl->setStartTime(start);
    stubCallImpl->startExecTimer();
  }
}

void StubImpl::update(std::time_t ms) {
  tick(ms);
  if (factory_->isDynamic()) {
    if ((status_ == StubStatus::UPDATING) && zeroref()) {
      auto *dynamicFactory = dynamic_cast<StubFactoryDynamic *>(factory_);
      // Upgrade failure
      if (!reset(dynamicFactory->getEntryFunc(), rpc_)) {
        // Already rollback to old one
        status_ = StubStatus::RESOLVED;
      }
      // Avoid some branch donot change status to RESOLVED
      if (status_ != StubStatus::RESOLVED) {
        setStatus(StubStatus::RESOLVED);
        return;
      }
    }
  }
  if (callQueue_.empty() || !zeroref()) {
    return;
  }
  auto callPtr = callQueue_.back();
  callQueue_.pop_back();
  callStub(callPtr);
}

void StubImpl::callStub(TransportPtr &transport, int bytes,
                        const RpcCallRequestHeader &header) {
  auto callPtr = rpc_->make_shared_ptr<StubCallImpl>();
  callPtr->setRpc(rpc_);
  callPtr->setTransport(transport);
  callPtr->setMethodID(header.methodID);
  callPtr->setCallID(header.callID);
  callPtr->setServiceID(serviceID_);
  callPtr->setServiceUUID(header.serviceUUID);
  callPtr->setData(transport, bytes);
  auto base = std::dynamic_pointer_cast<StubCall>(callPtr);
  callStub(base);
}

void StubImpl::callStubProxy(TransportPtr &transport, int bytes,
                             const RpcProxyCallRequestHeader &header) {
  auto callPtr = rpc_->make_shared_ptr<StubCallImpl>();
  callPtr->setRpc(rpc_);
  callPtr->setTransport(transport);
  callPtr->setMethodID(header.methodID);
  callPtr->setCallID(header.callID);
  callPtr->setServiceID(serviceID_);
  callPtr->setServiceUUID(header.serviceUUID);
  callPtr->setData(transport, bytes);
  callPtr->setGlobalIndex(header.globalIndex);
  auto base = std::dynamic_pointer_cast<StubCall>(callPtr);
  callStub(base);
}

void StubImpl::callStub(StubCallPtr &callPtr) {
  if (type_ != StubType::REENTRANT) {
    if (!zeroref()) {
      callQueue_.push_front(callPtr);
      return;
    }
  }
  incref();
  if (rpc_->isCoroMode() && (type_ != StubType::GENERIC)) {
    callCoro(callPtr);
  } else {
    callNormal(callPtr);
  }
}

void StubImpl::decref() { reference_ -= 1; }

void StubImpl::incref() { reference_ += 1; }

bool StubImpl::zeroref() { return (reference_ == 0); }

void StubImpl::setType(StubType type) { type_ = type; }

StubType StubImpl::getType() { return type_; }

ServiceID StubImpl::getServiceID() { return serviceID_; }

void StubImpl::setServiceID(ServiceID serviceID) { serviceID_ = serviceID; }

void StubImpl::setEntryFunc(BundleEntryFunc entryFunc) {
  entryFunc_ = entryFunc;
}

BundleEntryFunc StubImpl::getEntryFunc() { return entryFunc_; }

void StubImpl::setStatus(StubStatus status) { status_ = status; }

StubStatus StubImpl::getStatus() { return status_; }

void StubImpl::setFactory(StubFactory *factory) { factory_ = factory; }

StubFactory *StubImpl::getFactory() { return factory_; }

void StubImpl::setContext(kratos::service::ServiceContext *ctx) { ctx_ = ctx; }

kratos::service::ServiceContext *StubImpl::getContext() { return ctx_; }

void StubImpl::setRpc(Rpc *rpc) { rpc_ = dynamic_cast<RpcImpl *>(rpc); }

Rpc *StubImpl::getRpc() { return rpc_; }

StubCallImpl::~StubCallImpl() {
  if (data_) {
    rpc_->free(data_);
  }
  data_ = nullptr;
}

void StubCallImpl::finish() {
  if (finished_) {
    return;
  }
  finished_ = true;
  rpc_->getStubCallManager()->destroy(stubCallID_);
}

bool StubCallImpl::startExecTimer() {
  auto handle = rpc_->getStubCallManager()->getTimer()->addTimerOnce(
      [&](std::uint64_t callID) {
        auto stubCall = rpc_->getStubCallManager()->get(callID);
        if (!stubCall) {
          return;
        }
        auto stubCallImpl = std::dynamic_pointer_cast<StubCallImpl>(stubCall);
        auto stub =
            rpc_->getStubManager()->getServiceByID(stubCall->getServiceID());
        if (stub) {
          Logbegin << "Service execution timeout "
                   << "[" << stub->getServiceName() << ":"
                   << stub->getMethodName(stubCall->getMethodID()) << "]"
                   << Logend;
          auto stubImpl = std::dynamic_pointer_cast<StubImpl>(stub);
          stubImpl->decref();
        }
        if (stub && rpc_->isOpenStat()) {
          getStatisticImpl()->addStatistics(
              stub->getUUID(), stubCall->getMethodID(),
              stubCallImpl->getStartTime(), getMicroseconds(),
              stub->getMethodName(stubCallImpl->getMethodID()), false);
        }
        rpc_->getStubCallManager()->destroy(stubCall->getStubCallID());
      },
      RPC_TIMEOUT, stubCallID_);
  timerHandle_ = handle;
  return (handle != nullptr);
}

void StubCallImpl::stopExecTimer() {
  if (timerHandle_) {
    rpc_->getStubCallManager()->getTimer()->cancelTimer(timerHandle_);
    if (coroID_ != coroutine::INVALID_CORO_ID) {
      coro_close(coroID_);
    }
    coroID_ = coroutine::INVALID_CORO_ID;
  }
  timerHandle_ = nullptr;
}

void StubCallImpl::notFound() { rpc_->notFound(transport_, callID_); }

GlobalIndex StubCallImpl::getGlobalIndex() { return globalIndex_; }

Rpc *StubCallImpl::getRpc() { return rpc_; }

void StubCallImpl::clean() {
  stopExecTimer();
  if (coroID_ != coroutine::INVALID_CORO_ID) {
    coro_close(coroID_);
    coroID_ = coroutine::INVALID_CORO_ID;
  }
}

void StubCallImpl::setStartTime(std::time_t ms) { startCallTime_ = ms; }

std::time_t StubCallImpl::getStartTime() { return startCallTime_; }

void buildHeader(RpcRetHeader &rheader, rpc::StubCallPtr stubCall,
                 rpc::ServiceID serviceID, rpc::ErrorID errorID,
                 std::uint32_t bodySize) {
  rheader.header.length = sizeof(RpcRetHeader) + bodySize;
  rheader.header.type =
      static_cast<rpc::MsgTypeID>(rpc::RpcMsgType::RPC_RETURN);
  rheader.retHeader.serviceID = serviceID;
  rheader.retHeader.callID = stubCall->getCallID();
  rheader.retHeader.errorID = errorID;
  rheader.hton();
}

void buildHeader(RpcRetHeader &rheader, rpc::CallID callID,
                 rpc::ServiceID serviceID, rpc::ErrorID errorID,
                 std::uint32_t bodySize) {
  rheader.header.length = sizeof(RpcRetHeader) + bodySize;
  rheader.header.type =
      static_cast<rpc::MsgTypeID>(rpc::RpcMsgType::RPC_RETURN);
  rheader.retHeader.serviceID = serviceID;
  rheader.retHeader.callID = callID;
  rheader.retHeader.errorID = errorID;
  rheader.hton();
}

void buildHeader(RpcProxyRetHeader &rheader, rpc::StubCallPtr stubCall,
                 rpc::ServiceID serviceID, rpc::ErrorID errorID,
                 std::uint32_t bodySize) {
  rheader.header.length = sizeof(RpcProxyRetHeader) + bodySize;
  rheader.header.type =
      static_cast<rpc::MsgTypeID>(rpc::RpcMsgType::RPC_PROXY_RETURN);
  rheader.retHeader.serviceID = serviceID;
  rheader.retHeader.callID = stubCall->getCallID();
  rheader.retHeader.errorID = errorID;
  rheader.retHeader.globalIndex = stubCall->getGlobalIndex();
  rheader.hton();
}

void StubCallImpl::setGlobalIndex(GlobalIndex globalIndex) {
  globalIndex_ = globalIndex;
}

void StubCallImpl::setRpc(Rpc *rpc) { rpc_ = dynamic_cast<RpcImpl *>(rpc); }

StubFactoryDynamic::~StubFactoryDynamic() {
  for (auto &ci : classList_) {
    unload(ci);
  }
}

bool StubFactoryDynamic::isDynamic() { return true; }

StubManagerImpl::StubManagerImpl(RpcImpl *rpc) { rpc_ = rpc; }

StubManagerImpl::~StubManagerImpl() {}

StubPtr StubManagerImpl::addService(StubPtr stub) {
  if (!stub->onAfterFork(rpc_)) {
    return nullptr;
  }
  auto stubImpl = std::dynamic_pointer_cast<StubImpl>(stub);
  stubImpl->setRpc(rpc_);
  stubImpl->setServiceID(serviceID_++);
  typeMap_[stub->getUUID()].services.push_back(stub);
  serviceMap_.emplace(stubImpl->getServiceID(), stub);
  return stub;
}

void StubManagerImpl::setStatus(ServiceUUID uuid, StubStatus status) {
  for (auto &it : serviceMap_) {
    if (it.second->getUUID() == uuid) {
      it.second->setStatus(status);
    }
  }
}

StubPtr StubManagerImpl::getServiceByUUID(ServiceUUID uuid) {
  auto it = typeMap_.find(uuid);
  if (it == typeMap_.end()) {
    return nullptr;
  }
  if (it->second.services.empty()) {
    return nullptr;
  }
  if (it->second.index >= it->second.services.size()) {
    it->second.index = 0;
  }
  return it->second.services[it->second.index++];
}

StubPtr StubManagerImpl::getServiceByID(ServiceID id) {
  auto it = serviceMap_.find(id);
  if (it == serviceMap_.end()) {
    return nullptr;
  }
  return it->second;
}

void StubManagerImpl::update(std::time_t ms) {
  auto tick_ms = ms;
  if (!tick_ms) {
    tick_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
                  std::chrono::system_clock::now().time_since_epoch())
                  .count();
  }
  for (auto &it : serviceMap_) {
    it.second->update(tick_ms);
  }
}

ServiceList &StubManagerImpl::getMutableServiceMapUnsafe(ServiceUUID uuid) {
  static ServiceList EmptyList;
  auto it = typeMap_.find(uuid);
  if (it == typeMap_.end()) {
    return EmptyList;
  }
  return it->second.services;
}

void StubManagerImpl::removeAllServiceByUUID(ServiceUUID uuid) {
  for (auto it = serviceMap_.begin(); it != serviceMap_.end();) {
    if (it->second->getUUID() == uuid) {
      it = serviceMap_.erase(it);
    } else {
      it++;
    }
  }
}

void StubManagerImpl::resetAllStub(ServiceUUID uuid, BundleEntryFunc func) {
  auto it = typeMap_.find(uuid);
  if (it == typeMap_.end()) {
    return;
  }
  for (auto &stub : it->second.services) {
    stub->reset(func, rpc_);
  }
}

void StubManagerImpl::cleanup() {
  for (auto &it : serviceMap_) {
    it.second->onBeforeDestory(rpc_);
  }
  serviceMap_.clear();
  typeMap_.clear();
}

StubCallManagerImpl::StubCallManagerImpl(RpcImpl *rpc) {
  rpc_ = rpc;
  callMap_ = rpc_->allocObject<CallMap>();
}

void StubCallManagerImpl::update(std::time_t ms) {
  while (timer_.runOnce(ms)) {
  }
}

void StubCallManagerImpl::add(StubCallPtr call) {
  auto stubCallImpl = std::dynamic_pointer_cast<StubCallImpl>(call);
  callMap_->insert(std::make_pair(callID_, stubCallImpl));
  stubCallImpl->setStubCallID(callID_++);
  stubCallImpl->setRpc(rpc_);
}

StubCallPtr StubCallManagerImpl::get(StubCallID id) {
  auto it = callMap_->find(id);
  if (it == callMap_->end()) {
    return nullptr;
  }
  return it->second;
}

void StubCallManagerImpl::destroy(StubCallID id) {
  auto it = callMap_->find(id);
  if (it == callMap_->end()) {
    return;
  }
  it->second->clean();
  callMap_->erase(id);
}

void StubCallManagerImpl::removeByServiceID(ServiceID serviceID) {
  for (auto it = callMap_->begin(); it != callMap_->end();) {
    if (it->second->getServiceID() == serviceID) {
      it = callMap_->erase(it);
    } else {
      it++;
    }
  }
}

TimerBase *StubCallManagerImpl::getTimer() { return &timer_; }

void StubCallManagerImpl::cleanup() {

  if (callMap_) {
    for (auto &call : *callMap_) {
      call.second->clean();
    }
    callMap_->clear();
    rpc_->deallocObject(callMap_);
  }
  callMap_ = nullptr;
  timer_.clear();
}

StubCreator::StubCreator() {}

StubCreator::~StubCreator() {}

StubPtr StubCreator::createService(Rpc *rpc, ServiceUUID uuid,
                                   kratos::service::ServiceContext *ctx) {
  auto it = factoryMap_.find(uuid);
  if (it == factoryMap_.end()) {
    return nullptr;
  }
  if (it->second->getType() == StubType::SINGLE || it->second->getType() == StubType::REENTRANT) {
    auto stubPtr = rpc->getStubManager()->getServiceByUUID(uuid);
    if (stubPtr) {
      return stubPtr;
    }
  }
  auto *stub = it->second->create(ctx);
  if (!stub) {
    return rpc->getStubManager()->getServiceByUUID(uuid);
  }
  auto stubImpl = dynamic_cast<StubImpl *>(stub);
  stubImpl->setRpc(rpc);
  auto ptr = rpc->getStubManager()->addService(std::shared_ptr<Stub>(stub));
  if (!ptr) {
    return nullptr;
  }
  return ptr;
}

StubPtr StubCreator::getService(Rpc *rpc, ServiceID serviceID) {
  return rpc->getStubManager()->getServiceByID(serviceID);
}

bool StubCreator::isExist(ServiceUUID uuid) {
  return (factoryMap_.find(uuid) != factoryMap_.end());
}

void StubFactoryDynamic::onStubDestroy() { return; }

const std::string &StubFactoryDynamic::getVersion() {
  if (curClass_.versionFunc) {
    version_ = curClass_.versionFunc();
    return version_;
  } else {
    return NullString;
  }
}

BundleEntryFunc StubFactoryDynamic::getEntryFunc() {
  return curClass_.entryFunc;
}

bool StubFactoryDynamic::load(Rpc *rpc, const std::string &bundlePath) {
  for (auto &ci : classList_) {
    if (ci.bundlePath == bundlePath) {
      ci.refCnt += 1;
      Logbegin << "Bundle already loaded [" << bundlePath << "]" << Logend;
      return true;
    }
  }
  ClassInfo ci;
  ci.bundlePath = bundlePath;
#ifdef WIN32
  ci.loadHandle = ::LoadLibraryA(bundlePath.c_str());
#else
  ci.loadHandle = dlopen(bundlePath.c_str(), RTLD_LAZY);
#endif // WIN32
  if (ci.loadHandle == INVALID_MODULE_HANDLE) {
    Logbegin << "Load bundle failed [" << bundlePath << "]" << Logend;
    return false;
  }
#ifdef WIN32
  auto regFunc = (BundleRegisterFunc)::GetProcAddress(ci.loadHandle,
                                                      DEFAULT_REGISTER_NAME);
#else
  auto regFunc =
      (BundleRegisterFunc)dlsym(ci.loadHandle, DEFAULT_REGISTER_NAME);
#endif
  if (regFunc == nullptr) {
    Logbegin << "Load bundle failed [" << bundlePath << "]" << Logend;
    Logbegin << "Cannot find register entry function [" << DEFAULT_REGISTER_NAME
             << "]" << Logend;
    unload(ci);
    return false;
  }
  auto uuid = regFunc(rpc->getLogger());
  if (!StubCreatorRef.isExist(uuid)) {
    unload(ci);
    Logbegin << "Bundle UUID is not exist in creator "
             << "[" << uuid << "][" << bundlePath << "]" << Logend;
    return false;
  }

#ifdef WIN32
  ci.entryFunc =
      (BundleEntryFunc)::GetProcAddress(ci.loadHandle, ci.entryName.c_str());
  ci.versionFunc = (BundleVersionFunc)::GetProcAddress(ci.loadHandle,
                                                       ci.versionName.c_str());
  ci.buildFunc =
      (BundleBuildFunc)::GetProcAddress(ci.loadHandle, ci.buildName.c_str());
#else
  ci.entryFunc = (BundleEntryFunc)dlsym(ci.loadHandle, ci.entryName.c_str());
  ci.versionFunc =
      (BundleVersionFunc)dlsym(ci.loadHandle, ci.versionName.c_str());
  ci.buildFunc = (BundleBuildFunc)dlsym(ci.loadHandle, ci.buildName.c_str());
#endif
  if (!ci.entryFunc) {
    unload(ci);
    Logbegin << "Entry function not found [" << bundlePath << "]" << Logend;
    return false;
  }
  if (!ci.versionFunc) {
    Logbegin << "Version function not found [" << bundlePath << "]" << Logend;
  }
  if (!ci.buildFunc) {
    Logbegin << "Build function not found [" << bundlePath << "]" << Logend;
  } else {
    auto buildType = ci.buildFunc();
#ifdef _DEBUG
    if (!buildType) {
      unload(ci);
      Logbegin << "Build type mismatch, need DEBUG build [" << bundlePath << "]"
               << Logend;
      return false;
    }
#else
    if (buildType) {
      unload(ci);
      Logbegin << "Build type mismatch, need RELEASE build [" << bundlePath
               << "]" << Logend;
      return false;
    }
#endif
  }
  classList_.push_back(curClass_);
  curClass_ = ci;
  // Now updating
  onStatusChange(rpc, StubStatus::UPDATING);
  return true;
}

void StubFactoryDynamic::unloadClass(bool all) {
  for (auto &ci : classList_) {
    unload(ci);
  }
  if (all) {
    unload(curClass_);
  }
  classList_.clear();
}

bool StubFactoryDynamic::unload(ClassInfo &ci) {
  if (ci.loadHandle == INVALID_MODULE_HANDLE) {
    return true;
  }
  ci.refCnt -= 1;
  if (ci.refCnt > 0) {
    return true;
  }

#ifdef WIN32
  if (FALSE == ::FreeLibrary(ci.loadHandle)) {
    Logbegin << "System function error, FreeLibrary" << Logend;
    if (FALSE == ::UnmapViewOfFile(ci.loadHandle)) {
      Logbegin << "System function error, UnmapViewOfFile" << Logend;
      return false;
    }
  }
#else
  dlclose(ci.loadHandle);
#endif // WIN32
  ci.loadHandle = INVALID_MODULE_HANDLE;
  ci.entryFunc = nullptr;
  Logbegin << "Unload bundle [" << ci.bundlePath << "]" << Logend;
  return true;
}

bool StubCreator::loadClass(Rpc *rpc, ServiceUUID uuid,
                            const std::string &bundlePath, bool noCreate,
                            kratos::service::ServiceContext *ctx) {
  auto it = factoryMap_.find(uuid);
  if (it == factoryMap_.end()) {
    Logbegin << "Load bundle from disk failed, stub not in factory [" << uuid
             << "]" << Logend;
    return false;
  }
  if (it->second->isDynamic()) {
    auto *dynamicFactory = dynamic_cast<StubFactoryDynamic *>(it->second.get());
    if (!dynamicFactory->load(rpc, bundlePath)) {
      Logbegin << "Load bundle from disk failed [" << bundlePath << "]"
               << Logend;
      return false;
    }
    if (!noCreate) {
      if (!createService(rpc, uuid, ctx)) {
        Logbegin << "Fork bundle stub failed [" << bundlePath << "]" << Logend;
        return false;
      }
    }
    return true;
  } else {
    Logbegin << "Try to load bundle from disk but stub is static ["
             << bundlePath << "]" << Logend;
    return false;
  }
}

bool StubCreator::unloadClass(ServiceUUID uuid) {
  auto it = factoryMap_.find(uuid);
  if (it == factoryMap_.end()) {
    return false;
  }
  if (it->second->isDynamic()) {
    auto *dynamicFactory = dynamic_cast<StubFactoryDynamic *>(it->second.get());
    dynamicFactory->unloadClass(true);
    dynamicFactory->unload();
  } else {
    return false;
  }
  return true;
}

StubFactory *StubCreator::getFactory(ServiceUUID uuid) {
  auto it = factoryMap_.find(uuid);
  if (it != factoryMap_.end()) {
    return it->second.get();
  }
  return nullptr;
}

void onStubStatusChange(Rpc *rpc, StubStatus status, ServiceUUID serviceUUID) {
  rpc->getStubManager()->setStatus(serviceUUID, status);
}

StubCallManagerImpl::~StubCallManagerImpl() {}

StubFactoryImpl::StubFactoryImpl(StubType type) { type_ = type; }

bool StubFactoryImpl::isDynamic() { return false; }

void StubFactoryImpl::onStubDestroy() { return; }

const std::string &StubFactoryImpl::getVersion() { return version_; }

void StubFactoryImpl::setVersion(const std::string &version) {
  version_ = version;
}

StubFactoryDynamicImpl::StubFactoryDynamicImpl(StubType type) { type_ = type; }

} // namespace rpc
